package org.anyin.gitee.cloud.center.gateway.locator;

import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.anyin.gitee.cloud.center.common.constants.GatewayConstants;
import org.anyin.gitee.cloud.center.common.dto.SysRouteDTO;
import org.anyin.gitee.cloud.lib.core.cache.CacheTemplate;
import org.anyin.gitee.cloud.lib.core.utils.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class RedisRouteDefinitionLocator implements InitializingBean {

    private InMemoryRouteDefinitionRepository repository;

    private CacheTemplate cacheTemplate;

    public RedisRouteDefinitionLocator(InMemoryRouteDefinitionRepository repository, CacheTemplate cacheTemplate) {
        this.repository = repository;
        this.cacheTemplate = cacheTemplate;
    }

    /**
     * 构建断言
     * @param route 路由信息
     * @return PredicateDefinition
     */
    private PredicateDefinition buildPredicateDefinition(SysRouteDTO route){
        Map<String, String> args = new HashMap<>(8);
        args.put("pattern", route.getPath());

        PredicateDefinition predicate = new PredicateDefinition();
        predicate.setName("Path");
        predicate.setArgs(args);
        return predicate;
    }

    /**
     * 构建忽略前缀的过滤器
     * @param route 路由信息
     * @return FilterDefinition
     */
    private FilterDefinition buildStripPrefixFilterDefinition(SysRouteDTO route){
        Map<String, String> args = new HashMap<>(8);
        // 默认只去掉1个前缀
        args.put(NameUtils.generateName(0), route.getStripPrefix() + "");

        FilterDefinition definition = new FilterDefinition();
        definition.setName("StripPrefix");
        definition.setArgs(args);
        return definition;
    }

    /**
     * 元数据信息
     * @param route 路由信息
     * @return metadata
     */
    private Map<String,Object> buildMetadata(SysRouteDTO route){
        Map<String,Object> metadata = Maps.newHashMap();
        metadata.put(GatewayConstants.SYS_ROUTE_AUTH_KEY, route.getIsAuth());
        return metadata;
    }

    private void reloadArgs(List<FilterDefinition> filterDefinitions){
        if(CollectionUtil.isEmpty(filterDefinitions)){
            return;
        }
        for(FilterDefinition definition : filterDefinitions){
            Map<String, String> args = new HashMap<>();
            int i = 0;
            for(Map.Entry<String, String> entry : definition.getArgs().entrySet()){
                args.put(NameUtils.generateName(i), entry.getKey());
                args.put(NameUtils.generateName(i+1), entry.getValue());
                i += 2;
            }
            definition.setArgs(args);
        }
    }

    public Mono<Void> refresh(){
        List<SysRouteDTO> routes = cacheTemplate.valueGetList(GatewayConstants.SYS_ROUTE_KEY, SysRouteDTO.class);
        log.info("loading dynamic route , count: {}", routes.size());
        for(SysRouteDTO route : routes){
            try {
                // 断言
                List<PredicateDefinition> predicates = Lists.newArrayList();
                PredicateDefinition predicateDefinition = buildPredicateDefinition(route);
                predicates.add(predicateDefinition);

                // 过滤器
                List<FilterDefinition> filters = Lists.newArrayList();
                FilterDefinition stripPrefixFilterDefinition = buildStripPrefixFilterDefinition(route);
                filters.add(stripPrefixFilterDefinition);
                if(StringUtils.isNotEmpty(route.getFilters())){
                    List<FilterDefinition> customFilters = JSONUtil.toList(route.getFilters(), FilterDefinition.class);
                    this.reloadArgs(customFilters);
                    filters.addAll(customFilters);
                }

                // 元数据
                Map<String, Object> metadata = this.buildMetadata(route);

                // 代理路径
                String targetUri = StringUtils.isNotEmpty(route.getUrl()) ? route.getUrl() : "lb://" + route.getServiceId();
                URI uri = UriComponentsBuilder.fromUriString(targetUri).build().toUri();

                // 构建路由信息
                RouteDefinition routeDefinition = new RouteDefinition();
                routeDefinition.setId(route.getRouteName());
                routeDefinition.setPredicates(predicates);
                routeDefinition.setUri(uri);
                routeDefinition.setFilters(filters);
                routeDefinition.setMetadata(metadata);
                this.repository.save(Mono.just(routeDefinition)).subscribe();
            }catch (Exception ex) {
                log.error("路由加载失败: name={}, error={}", ex.getMessage(), ex);
            }
        }
        return Mono.empty();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
         this.refresh();
    }
}
